package server;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.Semaphore;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.Timer;

//import net.jxta.discovery.DiscoveryEvent;
//import net.jxta.discovery.DiscoveryListener;
import net.jxta.discovery.DiscoveryService;
import net.jxta.document.AdvertisementFactory;
import net.jxta.endpoint.ByteArrayMessageElement;
import net.jxta.endpoint.Message;
import net.jxta.endpoint.MessageElement;
import net.jxta.exception.PeerGroupException;
import net.jxta.id.IDFactory;
import net.jxta.impl.id.UUID.ModuleClassID;
import net.jxta.impl.id.UUID.PipeID;
import net.jxta.peer.PeerID;
import net.jxta.peergroup.PeerGroup;
import net.jxta.peergroup.PeerGroupID;
import net.jxta.pipe.OutputPipe;
import net.jxta.pipe.PipeMsgEvent;
import net.jxta.pipe.PipeMsgListener;
import net.jxta.pipe.PipeService;
import net.jxta.platform.Module;
import net.jxta.platform.NetworkConfigurator;
import net.jxta.platform.NetworkManager;
import net.jxta.protocol.ModuleClassAdvertisement;
import net.jxta.protocol.ModuleImplAdvertisement;
import net.jxta.protocol.ModuleSpecAdvertisement;
import net.jxta.protocol.PipeAdvertisement;


public class Server implements PipeMsgListener, ActionListener {

	
    public static void main(String[] args) throws PeerGroupException, IOException {

        Logger.getLogger("net.jxta").setLevel(Level.OFF);
        int port = 9000 + new Random().nextInt(100);

        Server server;
        
        server = new Server(port);
        server.start(); 
        server.fetch_advertisements();
    }


    private String peer_name;
    private PeerID peer_id;
    private File conf;
    private NetworkManager manager;
    private Vector<FNode> nodelist;
    private Semaphore m;
    private Timer counter;
    private int save;

    public Server(int port) {
    	
    	nodelist =  new Vector<FNode>();
    	m = new Semaphore(1);
    	
    	counter = new Timer(10000, this);
    	counter.setInitialDelay(10000);
    	
        peer_name = "Peer " + new Random().nextInt(1000000); 
        peer_id = IDFactory.newPeerID(PeerGroupID.defaultNetPeerGroupID, peer_name.getBytes());
        conf = new File("." + System.getProperty("file.separator") + peer_name);

        try {
            manager = new NetworkManager(NetworkManager.ConfigMode.ADHOC,peer_name, conf.toURI());
        }
        catch (IOException e) {
            e.printStackTrace();
        }

        NetworkConfigurator configurator;
        try {
            configurator = manager.getConfigurator();
            configurator.setTcpPort(port);
            configurator.setTcpEnabled(true);
            configurator.setTcpIncoming(true);
            configurator.setTcpOutgoing(true);
            configurator.setUseMulticast(true);
            configurator.setPeerID(peer_id);
        } 
        catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static final String subgroup_name = "P2PGroup";
    private static final String subgroup_desc = "...";
    private static final PeerGroupID subgroup_id = IDFactory.newPeerGroupID(PeerGroupID.defaultNetPeerGroupID, subgroup_name.getBytes());

    private static final String unicast_name = "SERVER:SERVER";
    private static final String service_name = "server";

    private PeerGroup subgroup;
    private PipeService pipe_service;
    private PipeID unicast_id;
    private PipeID service_id;
    private DiscoveryService discovery;
    private ModuleSpecAdvertisement mdadv;


    public void start() throws PeerGroupException, IOException {

        PeerGroup net_group = manager.startNetwork();
        
        ModuleImplAdvertisement mAdv = null;
        try {
            mAdv = net_group.getAllPurposePeerGroupImplAdvertisement();
        } catch (Exception ex) {
            System.err.println(ex.toString());
        }
        subgroup = net_group.newGroup(subgroup_id, mAdv, subgroup_name, subgroup_desc);

        if (Module.START_OK != subgroup.startApp(new String[0]))
            System.err.println("Cannot start child peergroup");

        unicast_id = (PipeID) IDFactory.newPipeID(subgroup.getPeerGroupID(), unicast_name.getBytes());

        pipe_service = subgroup.getPipeService();
        pipe_service.createInputPipe(get_advertisement(unicast_id, false), this);
        
        discovery = subgroup.getDiscoveryService();       

        ModuleClassAdvertisement mcadv = (ModuleClassAdvertisement)
        AdvertisementFactory.newAdvertisement(ModuleClassAdvertisement.getAdvertisementType());

        mcadv.setName("NAMESERVER");
        mcadv.setDescription("Name service for dfile server");
        ModuleClassID mcID = (ModuleClassID) IDFactory.newModuleClassID();
        mcadv.setModuleClassID(mcID);

        discovery.publish(mcadv);
        discovery.remotePublish(mcadv);

        mdadv = (ModuleSpecAdvertisement)
                AdvertisementFactory.newAdvertisement(ModuleSpecAdvertisement.getAdvertisementType());
        mdadv.setName("NAMESERVER");
        mdadv.setVersion("Version 1.0");
        mdadv.setCreator("Grupo05");
        mdadv.setModuleSpecID(IDFactory.newModuleSpecID(mcID));
        mdadv.setSpecURI("localhost");

        service_id = (PipeID) IDFactory.newPipeID(subgroup.getPeerGroupID(), service_name.getBytes());
        PipeAdvertisement pipeadv = get_advertisement(service_id, false);
        mdadv.setPipeAdvertisement(pipeadv);

        discovery.publish(mdadv);
        discovery.remotePublish(mdadv);

    }

    private static PipeAdvertisement get_advertisement(PipeID id, boolean is_multicast) {
        PipeAdvertisement adv = (PipeAdvertisement )AdvertisementFactory.
            newAdvertisement(PipeAdvertisement.getAdvertisementType());
        adv.setPipeID(id);
        if (is_multicast)
            adv.setType(PipeService.PropagateType); 
        else 
            adv.setType(PipeService.UnicastType); 
        adv.setName("default");
        adv.setDescription("default");
        return adv;
    }


    private void send_to_peer(String message, String found_peer_id) {

        PipeAdvertisement adv = get_advertisement(unicast_id, false);

        Set<PeerID> ps = new HashSet<PeerID>();
        try {
            ps.add((PeerID)IDFactory.fromURI(new URI(found_peer_id)));
        } 
        catch (URISyntaxException e) {
            e.printStackTrace();
        }

        OutputPipe sender = null;
        try {
            sender = pipe_service.createOutputPipe(adv, ps, 10000);
        } 
        catch (IOException e) {
            e.printStackTrace();
        }

        Message msg = new Message();
        MessageElement fromElem = null;
        MessageElement msgElem = null;
        try {
            fromElem = new ByteArrayMessageElement("From", null, peer_id.toString().getBytes("ISO-8859-1"), null);
            msgElem = new ByteArrayMessageElement("Msg", null, message.getBytes("ISO-8859-1"), null);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }


         msg.addMessageElement(fromElem);
         msg.addMessageElement(msgElem);

         try {
            sender.send(msg);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void pipeMsgEvent(PipeMsgEvent event) {
    	int index; 	
        try {
            Message msg = event.getMessage();
            byte[] msgBytes = msg.getMessageElement("Msg").getBytes(true);  
            byte[] fromBytes = msg.getMessageElement("From").getBytes(true); 
            String from = new String(fromBytes);
            String message = new String(msgBytes);
            
            System.out.println("REC:" + message );
            
            String[] result = message.split("\\s");
            
            if( result[0].equals("CREATE") ){
            	System.out.println("CREATING FILE");
            	index = inList(result[1]);
            	if( index == -1 ){
            		nodelist.add(new FNode(from, result[1]));
            		System.out.println("FILE CREATED");
            	} else {
            		StringBuffer strb = new StringBuffer();
            		strb.append("DELETE ");
            		strb.append(result[1]);
            		System.out.println("SENT: " + strb);
            		send_to_peer(strb.toString(), from);
            	}
            }
            
            if( result[0].equals("DELETE") ){
            	index = inList(result[1]);
            	if( index != -1 ){
            		StringBuffer strb = new StringBuffer();
            		strb.append("DELETE ");
            		strb.append(result[1]);
            		send_to_peer(strb.toString(), nodelist.get(index).getPeer_id());
            		nodelist.remove(index);
            	} else {
            		send_to_peer("NFOUND", from);
            	}
            }
            
            if( result[0].equals("READ") ){
            	m.acquire();
            	index = inList(result[1]);
            	if( index != -1 ){
            		if( nodelist.get(index).getLock() == 0 ){
            			nodelist.get(index).lockfile();
            			save = index;
                    	counter.start();
            			StringBuffer strb = new StringBuffer();
            			strb.append("ID ");
            			strb.append(nodelist.get(index).getPeer_id());
            			send_to_peer(strb.toString(), from);
            		} else {
            			send_to_peer("BUSY", from);
            		}
            	} else {
            		send_to_peer("NFOUND", from);
            	}
            	m.release();
            }
            
            if( result[0].equals("DONE")){
            	index = inList(result[1]);
            	if( index != -1 ){
            		if( nodelist.get(index).getLock() == 1 ){
            			counter.restart();
            			counter.stop();
            			nodelist.get(index).unlockfile();
            		}
            	}
            
            }
            
            if( result[0].equals("CHECK")){
            	send_to_peer("CHECKED", from);
            }
            
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    private int inList(String file){
    	
    	int size = nodelist.size();
    	for(int i = 0; i < size; i++ ){
    		if( file.equals(nodelist.get(i).getFile_id()) ){
    			return i;
    		}
    	}
    	return -1;
    }
    
    private void fetch_advertisements() {
        new Thread("fetch advertisements thread") {
           public void run() {
              while(true) {
                  discovery.getRemoteAdvertisements(null, DiscoveryService.ADV, "Name", "NAMESERVER", 1, null);
                  try {
                      sleep(10000);
                  }
                  catch(InterruptedException e) {} 
              }
           }
        }.start();
     }

	@Override
	public void actionPerformed(ActionEvent arg0) {
		System.out.println("File use time out");
		nodelist.get(save).unlockfile();
		counter.restart();
		counter.stop();
		
	}

}